Fix built-in function signatures overridden by vendor stubs#5440
Fix built-in function signatures overridden by vendor stubs#5440bartech wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
When incorrect stubs for core PHP functions are present in vendor/ (e.g. jetbrains/phpstorm-stubs installed transitively via roave/better-reflection), BetterReflection resolves built-in PHP functions like substr() and str_replace() from those stub files. Since these come from .php files, isInternal() returns false, and the signature map corrections introduced in 326c6ec are skipped. This causes false positives when the vendor stubs have incorrect signatures (e.g. optional parameters without default values). The fix adds a function_exists() check: if the function exists in PHP's runtime, it is a core built-in that cannot be redeclared, so signature corrections should always apply. The isInternal() guard is preserved for functions from unloaded PECL extensions, which CAN be redeclared by userland code. Fixes phpstan/phpstan#14450
|
@ondrejmirtes Sure thing but this throws away all symbol discovery from my dependencies. ./vendor/bin/phpstan analyse --configuration=.phpstan/local-config.neon --level=2What's wrong with the PR? I need symbol discovery. What I don't need is dependencies overwriting core php functions with wrong stubs definitions. |
|
Make sure to use https://github.com/szepeviktor/phpstan-wordpress. Does it help? |
|
I'm already using |
Fixes phpstan/phpstan#14450
Problem
Since 2.1.29 (commit 326c6ec),
NativeFunctionReflectionProviderskips signature map corrections for functions whereisInternal()returns false. This was correct for userland classes/methods sharing names with PECL extensions (#13556, #12151, #11303, #9486).However, when any vendor package ships incorrect stubs for core PHP functions (e.g.
jetbrains/phpstorm-stubswith optional parameters lacking default values), BetterReflection resolves built-in functions likesubstr()andstr_replace()from those vendor.phpfiles. Since they come from files,isInternal()returns false, and PHPStan's correct signature corrections are skipped — causing false-positivearguments.counterrors.jetbrains/phpstorm-stubsis the most common case — it arrives as a transitive dependency viaroave/better-reflection(12.6M downloads, 100+ dependents includingroave/backward-compatibility-check,kcs/class-finder,php-tui/php-tui,lucasbustamante/stubz). Users never install it directly and have no reason to know it's in their vendor directory.JetBrains/phpstorm-stubs#1863 fixes 199 incorrect signatures but is merged and not yet released (latest release: v2025.3, September 2025).
Fix
Added a
function_exists()check: if the function exists in PHP's runtime, it is a core built-in that cannot be redeclared (function substr() {}is a fatal error), so signature corrections should always apply.The
isInternal()guard is preserved for functions from unloaded PECL extensions (e.g.swf_actiongotoframe,http_redirect), which CAN be redeclared —function_exists()correctly returns false for those.Why
function_exists()is safePhpStormStubsMap.php(loaded via Composerautoload_files) is a class with constant arrays — it does not define functions.function_exists()is unaffected by stub loading.function_exists() = trueand cannot be redeclared.function_exists() = trueandisInternal() = true— already handled by the existing guard.function_exists() = false— guard correctly skips corrections.disable_functionsare removed from PHP's function table —function_exists() = false— guard correctly skips corrections (they can be redeclared).Test
Bug14450Testuses ascanFilesconfig to load a stub file with intentionally incorrect signatures (simulating any vendor package with wrong stubs). The test fails without the fix (reportsstr_replaceandsubstrerrors) and passes with the fix.Reproduction repository: https://github.com/bartech/phpstan-14450-repro